#ifndef __IM_EVENT_H__
#define __IM_EVENT_H__

#include <im_base_types.h>
#include <im_object.h>
#include <im_value.h>

/**
 * @defgroup IMEvent
 * @ingroup PublicIME
 * @{
 */

/** @struct IMEvent
 *  @brief IMEvent class is used to hold specified information as well as associated data of
 *         an IMBUS event.
 *
 * An IMEvent object contains the following fixed information:
 * - Event Type ID (IMUInt32)
 *   Static defined Type ID of the event, indicating the purpose of the event.
 *   Besides predefined events, IMBUS components can define their own user-defined
 *   event types. See im_event_types.h for details.
 *
 * - Event Source ID (IMUInt32)
 *   Unique ID of the source component
 *   (who generates the event, the event producer).
 *   The ID is allocated by IMBUS, so it's guaranteed that it's unique inside
 *   an IMBUS session.
 *   Following special source IDs are defined:
 *   - IM_EVENT_SOURCE_IMBUS
 *     Indicates that the event is generated by IMBUS itself.
 *   - IM_EVENT_SOURCE_INVALID
 *     Indicates an invalid source. For example when a component hasn't got
 *     an ID from IMBUS, this special ID should be used when sending any
 *     events.
 *
 * - Event Destination ID (IMUInt32)
 *   Unique ID of the destination component
 *   (who receives the event, the event consumer).
 *   The ID is allocated by IMBUS, so it's guaranteed that it's unique inside
 *   an IMBUS session. Following special destination IDs are defined:
 *   - IM_EVENT_DEST_DEFAULT (IM_EVENT_DEST_IMBUS)
 *     Let IM-BUS to choose the prefered destination component, which is, in
 *     most case, the input method component bound to the specified input
 *     context. If the event is not bound to an input context, then in most
 *     case IMBUS itself will consume the event.
 *   - IM_EVENT_DEST_BROADCAST
 *     Broadcast the event to all components who are capable to consume the
 *     event. If the event needs reply, then the source component may receive
 *     many replies from many destination components. So this special
 *     destination is usually used for non-reply events.
 *     If the event is bound to a specified input context (see below), then
 *     this special destination has no effect at all. IM_EVENT_DEST_DEFAULT
 *     should be used in this case.
 *   The destination component can only be event consumer. All observer
 *   components of the event will receive it no matter what destination id is
 *   used, unless Observable is set to FALSE (see below).
 *
 * - Original Destination ID (IMUInt32)
 *   Unique ID of the original destination component for an observed event.
 *   This field is only valid when an event is sent to an observer. In this
 *   case, this field indicates the original destination of the event
 *   (event consumer).
 *   For the events that sent to event consumer directly, this field shall be
 *   set to the same value as "Event destination ID", or IM_EVENT_DEST_DEFAULT.
 *   Actually, this field shall be ignored by the event consumer.
 *   For the events that sent to event observer, this field will be set to the
 *   ID of original event consumer by IMBUS. The observer can determine if it's
 *   an observed event by checking this field.
 *
 * - Input Context ID (ICID) (IMUInt32)
 *   Unique ID of the input context which the event is bound to. ICID is allocated
 *   by IMBUS in sequence. There are two special ICID:
 *   - IM_EVENT_INPUT_CONTEXT_NONE
 *     Indicates the event is not bound to any input context.
 *   - IM_EVENT_INPUT_CONTEXT_FOCUSED
 *     Indicates the currently focused input context.
 *
 * - TimeStamp
 *   A timestamp indicating when the event is created (generated),
 *   in milliseconds.
 *
 * - Serial Number (IMUInt32)
 *   A serial number indicating the sequence of the event. It's assigned
 *   by IMBUS.
 *
 * - Observable (IMBool)
 *   Indicates if the event is observable by the components other than the
 *   destination component. An observable event can be observed by any
 *   components which are interested in it (event observers).
 *   All events are observable by default.
 *
 * - Is reply (IMBool)
 *   Indicates if the event is a reply event of corresponding event type.
 *   For synchronous events, sender should always wait for reply event before
 *   continue.
 *
 * The source and destination ID can be a special ID indicating the IMBUS itself.
 *
 * Besides the above fixed information, an event may contain arbitrary number of data
 * with arbitrary type. Buf for a specified event type, the data type and number are
 * fixed.
 */

IM_BEGIN_DECLS
/** @brief cast the pointer to IMEvent*, return NULL if failed. */
#define IM_EVENT(p)                  (IM_TYPE_INSTANCE_CHECK_CAST((p),IM_TYPE_EVENT,IMEvent))
/** @brief cast the pointer to const IMEvent*, return NULL if failed. */
#define IM_CONST_EVENT(p)            (IM_TYPE_INSTANCE_CHECK_CAST_CONST((p),IM_TYPE_EVENT,IMEvent))
/** @brief cast the pointer to IMEventClass*, return NULL if failed. */
#define IM_EVENT_CLASS(c)            (IM_TYPE_CLASS_CHECK_CAST((c),IM_TYPE_EVENT,IMEventClass))
/** @brief tell if the pointer is an instance of IMEvent. */
#define IM_IS_EVENT(p)               (IM_TYPE_INSTANCE_CHECK_TYPE((p),IM_TYPE_EVENT))
/** @brief tell if the pointer is an IMEventClass. */
#define IM_IS_EVENT_CLASS(c)         (IM_TYPE_CLASS_CHECK_TYPE((c),IM_TYPE_EVENT))
/** @brief get the type id from an instance of IMEvent. */
#define IM_EVENT_GET_CLASS(p)        (IM_TYPE_INSTANCE_GET_CLASS_CAST((p),IM_TYPE_EVENT,IMEventClass))

typedef struct _IMEvent         IMEvent;
typedef struct _IMEventClass    IMEventClass;
typedef struct _IMEventInfo     IMEventInfo;

/** @struct IMEventInfo
 * @brief This structure holds various information of an
 *        predefined event type. Such as number data
 *        and the type of each data that the event should
 *        contain.
 */
struct _IMEventInfo
{
    IMUInt32        type;
    IMBool          reply;
    IMSize          num_of_data;
    const IMType   *data_types;
};

enum
{
    IM_EVENT_SOURCE_IMBUS   = 0,
    IM_EVENT_SOURCE_INVALID = IM_MAXUINT32
};

enum
{
    IM_EVENT_DEST_IMBUS     = 0,
    IM_EVENT_DEST_DEFAULT   = 0,
    IM_EVENT_DEST_BROADCAST = IM_MAXUINT32
};

enum
{
    IM_EVENT_INPUT_CONTEXT_NONE     = 0,
    IM_EVENT_INPUT_CONTEXT_FOCUSED  = IM_MAXUINT32
};

/**
 * @brief Create a new IMEvent object with the given type.
 *
 * @param type          event type id
 *
 * @return  the new IMEvent
 */
IMEvent*        im_event_new                    (IMUInt32            type,
                                                 IMBool              reply);

/**
 * @brief Create a new IMEvent object with full parameters.
 *
 * @param type          event type id
 * @param src_id        source id
 * @param dest_id       destination id
 * @param ic_id         InputContext id
 *
 * @return  the new IMEvent
 */
IMEvent*        im_event_new_full               (IMUInt32            type,
                                                 IMUInt32            src_id,
                                                 IMUInt32            dest_id,
                                                 IMUInt32            ic_id,
                                                 IMBool              observable,
                                                 IMBool              reply);

/**
 * @brief Clear the data of an event.
 *
 * All fixed event information won't be cleared.
 * Only the arbitrary data of the event will be cleared.
 */
void            im_event_clear_data             (IMEvent            *event);

/**
 * @brief Set event type of an event object.
 *
 * All data owned by the event will be cleared,
 */
void            im_event_set_type               (IMEvent            *event,
                                                 IMUInt32            type);
/**
 * @brief Get event type of an event object.
 */
IMUInt32        im_event_get_type               (const IMEvent      *event);

/**
 * @brief Set source id of an event object.
 */
void            im_event_set_source             (IMEvent            *event,
                                                 IMUInt32            src_id);
/**
 * @brief Get source id of an event object.
 */
IMUInt32        im_event_get_source             (const IMEvent      *event);

/**
 * @brief Set destination id of an event object.
 */
void            im_event_set_destination        (IMEvent            *event,
                                                 IMUInt32            dest_id);
/**
 * @brief Get destination id of an event object.
 */
IMUInt32        im_event_get_destination        (const IMEvent      *event);

/**
 * @brief Set original destination id of an event object.
 */
void            im_event_set_original_destination(IMEvent            *event,
                                                  IMUInt32            orig_dest_id);
/**
 * @brief Get original destination id of an event object.
 */
IMUInt32        im_event_get_original_destination(const IMEvent      *event);

/**
 * @brief Set serial number of an event object.
 *
 * This method should only be called by IMBUS daemon.
 */
void            im_event_set_serial_number      (IMEvent            *event,
                                                 IMUInt32            serial);
/**
 * @brief Get serial number of an event object.
 */
IMUInt32        im_event_get_serial_number      (const IMEvent      *event);

/**
 * @brief Set InputContext ID of an event object.
 */
void            im_event_set_input_context_id   (IMEvent            *event,
                                                 IMUInt32            icid);
/**
 * @brief Get InputContext ID of an event object.
 */
IMUInt32        im_event_get_input_context_id   (const IMEvent      *event);

/**
 * @brief Update timestamp of an event object to the current time.
 */
void            im_event_update_timestamp       (IMEvent            *event);

/**
 * @brief Set timestamp of an event object to a specified time.
 * in milliseconds.
 */
void            im_event_set_timestamp          (IMEvent            *event,
                                                 IMUInt64            timestamp);

/**
 * @brief Get timestamp of an event object.
 */
IMUInt64        im_event_get_timestamp          (const IMEvent      *event);

/**
 * @brief Set observable flag of an event object.
 */
void            im_event_set_observable         (IMEvent             *event,
                                                 IMBool               observable);

/**
 * @brief Get observable flag of an event object.
 */
IMBool          im_event_get_observable         (const IMEvent      *event);

/**
 * @brief Set is reply flag of an event object.
 */
void            im_event_set_is_reply           (IMEvent             *event,
                                                 IMBool               reply);

/**
 * @brief Get is reply flag of an event object.
 */
IMBool          im_event_get_is_reply           (const IMEvent      *event);

/**
 * @brief Get the number of data that a specified event contains.
 */
IMSize          im_event_get_n_data             (const IMEvent      *event);

/**
 * @brief Get the type of a data in an event at specified index.
 */
IMType          im_event_get_data_type          (const IMEvent      *event,
                                                 IMSize              index);
/**
 * @brief Get a data in an event at specified index.
 *
 * The data will be stored into given IMValue object,
 * and the data type will be returned.
 */
IMType          im_event_get_data               (const IMEvent      *event,
                                                 IMSize              index,
                                                 IMValue            *value);
/**
 * @brief Append a data ,which is stored in a specified IMValue object,
 *        into a specified event.
 */
void            im_event_append_data            (IMEvent            *event,
                                                 IMValue            *value);
/**
 * @brief Set a data in an event at specified index.
 */
void            im_event_set_data               (IMEvent            *event,
                                                 IMSize              index,
                                                 IMValue            *value);

/** @brief get a char at the given index in the data of an event */
IMChar          im_event_get_char               (const IMEvent      *event,
                                                 IMSize              index);
/** @brief get an unsigned char at the given index in the data of an event */
IMUChar         im_event_get_uchar              (const IMEvent      *event,
                                                 IMSize              index);
/** @brief get a boolean at the given index in the data of an event */
IMBool          im_event_get_bool               (const IMEvent      *event,
                                                 IMSize              index);
/** @brief get an int16 (short) at the given index in the data of an event */
IMInt16         im_event_get_int16              (const IMEvent      *event,
                                                 IMSize              index);
/** @brief get an unsigned int16 (short) at the given index in the data of an event */
IMUInt16        im_event_get_uint16             (const IMEvent      *event,
                                                 IMSize              index);
/** @brief get an int32 (long) at the given index in the data of an event */
IMInt32         im_event_get_int32              (const IMEvent      *event,
                                                 IMSize              index);
/** @brief get an unsigned int32 (long) at the given index in the data of an event */
IMUInt32        im_event_get_uint32             (const IMEvent      *event,
                                                 IMSize              index);
/** @brief get an int64 (long long) at the given index in the data of an event */
IMInt64         im_event_get_int64              (const IMEvent      *event,
                                                 IMSize              index);
/** @brief get an unsigned int64 (long long) at the given index in the data of an event */
IMUInt64        im_event_get_uint64             (const IMEvent      *event,
                                                 IMSize              index);
/** @brief get a doulbe value at the given index in the data of an event */
IMDouble        im_event_get_double             (const IMEvent      *event,
                                                 IMSize              index);
/** @brief get a c-string at the given index in the data of an event */
const IMChar*   im_event_get_c_string           (const IMEvent      *event,
                                                 IMSize              index);
/** @brief get a duplicated c-string at the given index in the data of an event */
IMChar*         im_event_dup_c_string           (const IMEvent      *event,
                                                 IMSize              index);
/** @brief get an object at the given index in the data of an event */
IMPointer       im_event_get_object             (const IMEvent      *event,
                                                 IMSize              index);
/** @brief get a duplicated object at the given index in the data of an event */
IMPointer       im_event_dup_object             (const IMEvent      *event,
                                                 IMSize              index);
/**
 * @brief Validate the type of each data contained by an event according to
 *        the event type id, to see whether they conform to the standard
 *        of predefined event types.
 */
IMBool          im_event_validate               (const IMEvent      *event);

IM_END_DECLS

#endif
/**  @} */

/*
vi:ts=4:nowrap:ai:expandtab
*/
